<!DOCTYPE html>
<!--
  Author: bpearcy@google.com (Brandon Pearcy)
-->
<html>
<!--
Copyright 2009 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by an Apache 2.0 License.
See the COPYING file for details.
-->
<head>
<title>CrossPageChannel: End-to-End Test</title>
<script src="../../base.js"></script>
<script>
  goog.require('goog.Disposable');
  goog.require('goog.debug.Logger');
  goog.require('goog.dom');
  goog.require('goog.net.xpc.CrossPageChannel');
  goog.require('goog.testing.AsyncTestCase');
  goog.require('goog.testing.PropertyReplacer');
  goog.require('goog.testing.jsunit');
</script>
</head>
<body>
<script>

var IFRAME_LOAD_WAIT_MS = 2000;
var stubs = new goog.testing.PropertyReplacer();
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(
    document.title);
var uniqueId = 0;
var driver;


function setUpPage() {
  asyncTestCase.stepTimeout = 10 * 1000;

  // Show debug log
  var debugDiv = goog.dom.getElement('debugDiv');
  var logger = goog.debug.Logger.getLogger('goog.net.xpc');
  logger.setLevel(goog.debug.Logger.Level.ALL);
  logger.addHandler(function(logRecord) {
    var msgElm = goog.dom.createDom('div');
    msgElm.innerHTML = logRecord.getMessage();
    goog.dom.appendChild(debugDiv, msgElm);
  });
}


function setUp() {
  driver = new Driver();
}


function tearDown() {
  stubs.reset();
  driver.dispose();
}


function testCreateIframeSpecifyId() {
  driver.createPeerIframe('new_iframe');

  asyncTestCase.waitForAsync('iframe load');
  window.setTimeout(function() {
    asyncTestCase.continueTesting();
    driver.checkPeerIframe();
  }, IFRAME_LOAD_WAIT_MS);
}


function testCreateIframeRandomId() {
  driver.createPeerIframe();

  asyncTestCase.waitForAsync('iframe load');
  window.setTimeout(function() {
    asyncTestCase.continueTesting();
    driver.checkPeerIframe();
  }, IFRAME_LOAD_WAIT_MS);
}


function testCreateIframeAsynchronousConnect() {
  driver.createPeerIframe('new_iframe');

  asyncTestCase.waitForAsync('iframe load');
  window.setTimeout(function() {
    driver.connect();
  }, IFRAME_LOAD_WAIT_MS);
}


function testCreateIframeSynchronousConnect() {
  driver.createPeerIframe('new_iframe');
  driver.connect();
}


function testExistingIframeConnect() {
  driver.createChannel('test_iframe');
  driver.connect();
}


/**
 * Driver for the tests for CrossPageChannel.
 *
 * @constructor
 * @extends {goog.Disposable}
 */
Driver = function() {
  /**
   * The peer iframe.
   * @type {!Element}
   * @private
   */
  this.iframe_ = null;

  /**
   * The ID of the peer iframe.
   * @type {string}
   * @private
   */
  this.iframeId_ = 'test_iframe';

  /**
   * Channel configuration object.
   * @type {Object}
   * @private
   */
  this.cfg_ = null;

  /**
   * The channel to use.
   * @type {goog.net.xpc.CrossPageChannel}
   * @private
   */
  this.channel_ = null;
};
goog.inherits(Driver, goog.Disposable);


/**
 * Disposes of the channel.
 */
Driver.prototype.disposeInternal = function() {
  goog.net.xpc.CrossPageChannel.superClass_.disposeInternal.call(this);

  if (this.channel_) {
    this.channel_.dispose();
  }
};


/**
 * Returns the child peer's window object.
 * @return {Window} Child peer's window.
 * @private
 */
Driver.prototype.getInnerPeer_ = function() {
  return window.frames[this.iframeId_];
};


/**
 * Creates the channel.
 * @param {string} opt_iframeId If present, the ID of the iframe to use,
 *     otherwise, tells the channel to generate an iframe ID.
 */
Driver.prototype.createChannel = function(opt_iframeId) {
  var cfg = {};
  if (opt_iframeId) {
    this.iframeId_ = opt_iframeId;
    cfg[goog.net.xpc.CfgFields.IFRAME_ID] = opt_iframeId;
  }
  cfg[goog.net.xpc.CfgFields.PEER_URI] = 'testdata/inner_peer.html';
  cfg[goog.net.xpc.CfgFields.CHANNEL_NAME] = 'test_channel' + uniqueId++;
  cfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] = 'does-not-exist.html';
  cfg[goog.net.xpc.CfgFields.PEER_POLL_URI] = 'does-not-exist.html';

  this.channel_ = new goog.net.xpc.CrossPageChannel(cfg);
  this.channel_.registerService('msg', goog.bind(this.msgHandler_, this));
  this.cfg_ = cfg;
};


/**
 * Creates the peer iframe.
 * @param {string} opt_iframeId If present, the ID of the iframe to create,
 *     otherwise, generates an iframe ID.
 */
Driver.prototype.createPeerIframe = function(opt_iframeId) {
  var expectedIframeId;

  if (opt_iframeId) {
    expectedIframeId = opt_iframeId = opt_iframeId + uniqueId++;
  } else {
    // Have createPeerIframe() generate an ID
    stubs.set(goog.net.xpc, 'getRandomString', function(length) {
      return '' + length;
    });
    expectedIframeId = 'xpcpeer4';
  }
  assertNull('element[id=' + expectedIframeId + '] exists',
      goog.dom.getElement(expectedIframeId));

  this.createChannel(opt_iframeId);
  this.iframe_ = this.channel_.createPeerIframe(document.body);

  assertEquals(expectedIframeId, this.iframe_.id);
  this.iframeId_ = expectedIframeId;
};


/**
 * Checks if the peer iframe has been created.
 */
Driver.prototype.checkPeerIframe = function() {
  assertNotNull(goog.dom.getElement(this.iframeId_));
  var peer = this.getInnerPeer_();
  assertNotNull(peer);
  assertNotNull(peer.document);
};


/**
 * Starts the connection. The connection happens asynchronously.
 */
Driver.prototype.connect = function() {
  // TODO(user): Get tests to pass for all transports
  if (!this.isTransportTestable_()) {
    asyncTestCase.continueTesting();
    return;
  }

  if (this.iframe_) {
    // When we create an iframe, we send the config
    // and it connects itself.
    this.childConnected_();
  } else {
    asyncTestCase.waitForAsync('parent and child connect');
    var peer = this.getInnerPeer_();
    peer.instantiateChannel(this.cfg_);
    peer.connectChannel(goog.bind(this.childConnected_, this));
  }

  this.channel_.connect(goog.bind(this.parentConnected_, this));
};


/**
 * Determines if the transport type for the channel is testable.
 * Some transports are misusing global state or making other
 * assumptions that cause connections to fail.
 * @return {boolean} Whether the transport is testable.
 * @private
 */
Driver.prototype.isTransportTestable_ = function() {
  var testable = true;

  var transportType = this.channel_.determineTransportType_();
  switch (transportType) {
    case goog.net.xpc.TransportTypes.NATIVE_MESSAGING:
    case goog.net.xpc.TransportTypes.IFRAME_RELAY:
    case goog.net.xpc.TransportTypes.IFRAME_POLLING:
    case goog.net.xpc.TransportTypes.FLASH:
    case goog.net.xpc.TransportTypes.NIX:
      testable = false;
      break;
  }

  return testable;
};


/**
 * Callback for the parent connection.
 * @private
 */
Driver.prototype.parentConnected_ = function() {
  var peer = this.getInnerPeer_();
  if (peer.isConnected()) {
    asyncTestCase.waitForAsync('child echo');
  } else {
    asyncTestCase.waitForAsync('child connect');
  }

  this.channel_.send('echo', 'hello')
};


/**
 * Callback for the child connection.
 * @private
 */
Driver.prototype.childConnected_ = function() {
  if (this.channel_.isConnected()) {
    asyncTestCase.waitForAsync('child echo');
  } else {
    asyncTestCase.waitForAsync('parent connect');
  }
};


/**
 * The handler function for incoming msg requests.
 * @param {string} payload The message payload.
 * @private
 */
Driver.prototype.msgHandler_ = function(payload) {
  assertTrue('parent should be connected', this.channel_.isConnected());
  var peer = this.getInnerPeer_();
  assertTrue('child should be connected', peer.isConnected());
  assertEquals('hello', payload);

  asyncTestCase.continueTesting();
};


</script>

<!-- Debug box. -->
<div style="position:absolute; float: right; top: 10px; right: 10px">
Debug [<a href="#" onclick="document.getElementById('debugDiv').innerHTML = '';">clear</a>]: <br/>
<div id="debugDiv" style="border: 1px #000000 solid; font-size:xx-small"></div>
</div>

<!--
Setup inner peer. This must be done in the <body>, so that the inner peer iframe
blocks the onload event in this window until it is fully loaded. The JsUnit
tests will execute after the onload event fires.
-->
<iframe name=test_iframe id=test_iframe src=testdata/inner_peer.html width=300 height=250>
</iframe>

</body>
</html>
